#include "Controller.h"

#include <cv.h>
#include <highgui.h>


#include <deque>
#include <vector>
#include <string>

#include <tiff.h>
#include <tiffio.h>

#include <niftout/Output.h>

#include <CvUtils/ImageUtils.h>
#include <CvUtils/MathUtils.h>

#include "ErrorCodes.hpp"

#include "Gui.h"
#include "Input.h"
#include "input/PTOParser.h"
#include "Features.h"
#include "output/DataWriter.h"
#include "output/HomographyWriter.h"
#include "utils/Settings.h"
#include "utils/Output.h"

Controller::Controller()
{

}

Controller::~Controller()
{
}


errorCodes::errorCode Controller::init(bool useFile, std::string videoFileName){
	this->finished = false;
	
	//this->gui = new Gui(this);
	//this->input = new Input(useFile, videoFileName);
	this->parser = new PTOParser();
	
	if(this->gui == NULL || this->input == NULL) {
		return errorCodes::ERROR_ON_INIT;
	}
	
	return errorCodes::SUCCESS;
}

void Controller::setup(){
	/* Inherited from Thread; not needed here */
}

/**
 * The main method, inherited from Thread.
 * Called via start(void* arg), which is also inherited.
 **/
void Controller::execute(void* arg){
	
	this->parser->parse("input.pto");
	

	std::deque<std::string> imageNames;
	std::deque<Features::FeaturePair> featurePairs;
	std::deque<Features::FeaturePair> filteredPairs;
	std::deque<IplImage *> images;
	std::deque<CvSize> imageSizes;
	IplImage* currentImage;
	
	imageNames   = this->parser->getFileNames();
	featurePairs = this->parser->getFeaturePairs();
	
	for (unsigned int i = 0; i < imageNames.size(); ++i){
		this->loadImage(imageNames[i], currentImage);
		images.push_back(currentImage);
		imageSizes.push_back(cvGetSize(currentImage));
	}
	
	
	//TODO for all feature pairs
		//TODO get 40x40 area around each feature
		//TODO draw 17x17 neighborhood arour feature (cvRectangle)
		//TODO display subimages
		//TODO ask for keep/discard
	
	IplImage* leftArea;
	IplImage* rightArea;
	IplImage* overview;
	CvPoint start;
	CvPoint end;
	CvPoint rectStart;
	CvPoint rectEnd;
	CvPoint overviewStart;
	CvPoint overviewEnd;
	Features::FeaturePair currentPair;
	
	int zoomSize;
	int overviewSize;
	
	zoomSize = 60;
	overviewSize = 300;
	
	int answer = 0;
	
	for (unsigned int i = 0; i < featurePairs.size(); ++i) {
		currentPair = featurePairs[i];
		
		Output::message("Showing features (%f, %f) <-> (%f, %f) in images %d and %d", Output::DEBUG, currentPair.left.x, currentPair.left.y, currentPair.right.x, currentPair.right.y, currentPair.leftImageID, currentPair.rightImageID);
		
		start = cvPoint(MathUtils::fitRange(currentPair.left.x - zoomSize, 0, images[currentPair.leftImageID]->width), MathUtils::fitRange(currentPair.left.y - zoomSize, 0, images[currentPair.leftImageID]->height));
		end   = cvPoint(MathUtils::fitRange(currentPair.left.x + zoomSize, 0, images[currentPair.leftImageID]->width), MathUtils::fitRange(currentPair.left.y + zoomSize, 0, images[currentPair.leftImageID]->height));
		leftArea = ImageUtils::getSubImage(images[currentPair.leftImageID], start, end);
		
		Output::message("Start/end for left image: (%d, %d) -> (%d, %d)", Output::DEBUG, start.x, start.y, end.x, end.y);
		
		//rectStart = cvPoint(currentPair.left.x - 8, currentPair.left.y - 8);
		//rectEnd   = cvPoint(currentPair.left.x + 8, currentPair.left.y + 8);
		rectStart = cvPoint(zoomSize - 10, zoomSize - 10);
		rectEnd   = cvPoint(zoomSize + 10, zoomSize + 10);
		
		Output::message("Rect for left image: (%d, %d) -> (%d, %d)", Output::DEBUG, rectStart.x, rectStart.y, rectEnd.x, rectEnd.y);

		cvRectangle(leftArea, rectStart, rectEnd, CV_RGB(255,0,0), 1);
		
		
		start = cvPoint(MathUtils::fitRange(currentPair.right.x - zoomSize, 0, images[currentPair.rightImageID]->width), MathUtils::fitRange(currentPair.right.y - zoomSize, 0, images[currentPair.rightImageID]->height));
		end   = cvPoint(MathUtils::fitRange(currentPair.right.x + zoomSize, 0, images[currentPair.rightImageID]->width), MathUtils::fitRange(currentPair.right.y + zoomSize, 0, images[currentPair.rightImageID]->height));
		rightArea = ImageUtils::getSubImage(images[currentPair.rightImageID], start, end);
		
		Output::message("Start/end for left image: (%d, %d) -> (%d, %d)", Output::DEBUG, start.x, start.y, end.x, end.y);
		
//		rectStart = cvPoint(currentPair.right.x - 8, currentPair.right.y - 8);
//		rectEnd   = cvPoint(currentPair.right.x + 8, currentPair.right.y + 8);
		/*rectStart = cvPoint(10, 10);
		rectEnd   = cvPoint(30,30);*/
		
		Output::message("Rect for left image: (%d, %d) -> (%d, %d)", Output::DEBUG, rectStart.x, rectStart.y, rectEnd.x, rectEnd.y);

		cvRectangle(rightArea, rectStart, rectEnd, CV_RGB(255,0,0), 1);
		
		
		
		overviewStart = cvPoint(MathUtils::fitRange(currentPair.left.x - overviewSize, 0, images[currentPair.leftImageID]->width), MathUtils::fitRange(currentPair.left.y - overviewSize, 0, images[currentPair.leftImageID]->height));
		overviewEnd   = cvPoint(MathUtils::fitRange(currentPair.left.x + overviewSize, 0, images[currentPair.leftImageID]->width), MathUtils::fitRange(currentPair.left.y + overviewSize, 0, images[currentPair.leftImageID]->height));
		overview	  = ImageUtils::getSubImage(images[currentPair.leftImageID], overviewStart, overviewEnd);
		
		rectStart = cvPoint(overviewSize - 10, overviewSize - 10);
		rectEnd   = cvPoint(overviewSize + 10, overviewSize + 10);
		
		cvRectangle(overview, rectStart, rectEnd, CV_RGB(255,0,0), 1);
		
		
		
		cvNamedWindow("Left Feature", 0);
		cvNamedWindow("Right Feature", 0);
		cvNamedWindow("Overview", CV_WINDOW_AUTOSIZE);
		
		cvMoveWindow("Left Feature", 0, 0);
		cvMoveWindow("Right Feature", zoomSize*5 + 30, 0);
		cvMoveWindow("Overview", zoomSize*10 + 60, 0);
		
		cvShowImage("Left Feature",  leftArea);
		cvShowImage("Right Feature", rightArea);
		cvShowImage("Overview", overview);
		
		bool validAnswer; 
		do {
			/* if we hit any 'case', the answer is valid; only if we hit the 'default'
			 * statement an invalid answer was given.
			 * So we might as well set it to true here and only change it in the default
			 * block. */
			validAnswer = true;
			
			answer = cvWaitKey(0);
			switch ((char)answer){
			case 'Y':
			case 'y':
				///TODO save/keep pair
				filteredPairs.push_back(currentPair);
				break;
				
			case 'N':
			case 'n':
				///TODO discard pair
				break;
				
			case 'D':
			case 'd':
				///TODO disregard pair, i.e. don't throw away, but don't use for homography either
			
			default:
				/* if we hit this point,obviously the pressed key was
				 * nothing we expected. */
				Output::message("Answer: %c", Output::DEBUG, answer);
				validAnswer = false;
				break;
			}
		} while (!validAnswer);
		
		
	}
	
	
	///TODO show new sets of feature pairs
	
	///TODO checken, ob irgendein Bild komplett rausfliegt
		///TODO bool-array machen
		///TODO durch filteredPairs gehen, bei jeder leftID und rightID ->true setzen
		///TODO wenn irgendwo false steht
			///TODO durch alles Pairs gehen und alle höheren IDS dekrementieren
			///TODO entsprechenden Eintrag aus imageNames löschen
			///TODO sämtliche Werte im array eins nach links kopieren (da sich die IDs geändert haben)
			///TODO wiederholen, bis gesamten Array durchlaufen
	
	DataWriter* dataWriter = new DataWriter();
	
	dataWriter->writeToFile(imageNames, filteredPairs, imageSizes);
	
	
	
	/*
	 *  we need to discern the filtered feature pairs again, so we can compute homographies.
	 * (yes, it may have been easier to not put them in the same vector in the first place, but time
	 * is running short)
	 * 
	 * Idea: make a vector of all id combinations, and take the index of each element as index
	 * for the pair sets/homographies.
	 * For that, we take each feature pair and check if the id combination is already present in
	 * the vector. If so, we add the pair to the respective set, otherwise we add a further element
	 * to the vector with the respective id combination and start a new set.
	 */
	
	std::deque<Features::IDPair> idCombinations;
	std::deque< std::deque<Features::FeaturePair> > dissectedPairs;
	
	bool idCombinationFound;
	int combinationIndex;
	
	
	for (unsigned int i = 0; i < filteredPairs.size(); ++i) {
		idCombinationFound  = false;
		
		for (unsigned int j = 0; j < idCombinations.size(); ++j) {
			if (filteredPairs[i].rightImageID == idCombinations[j].rightImageID && 
				filteredPairs[i].leftImageID  == idCombinations[j].leftImageID){
				idCombinationFound = true;
				combinationIndex = j;
				break;
			}
		}
		
		if (!idCombinationFound){
			/* this ID pair is not present in the 'database', so add it. */
			Features::IDPair newPair;
			newPair.rightImageID = filteredPairs[i].rightImageID;
			newPair.leftImageID  = filteredPairs[i].leftImageID;
			idCombinations.push_back(newPair);
			
			std::deque<Features::FeaturePair> newPairSet;
			dissectedPairs.push_back(newPairSet);
			
			combinationIndex = idCombinations.size() - 1;
		}
		
		dissectedPairs[combinationIndex].push_back(filteredPairs[i]);
			
	}
	
	
	
	
	std::deque<CvMat*> homographies;
	
	for (unsigned int i = 0; i < dissectedPairs.size(); ++i) {
		
		/* homography will always be 3x3 */
		/* the result of findHomohgraphy may be NULL. This has to be filtered in a later step.
		 * We can't do that here, because we need the same indices as the dissectedPairs. */
		homographies.push_back(findHomography(dissectedPairs[i]));
	}
	
	HomographyWriter* homographyWriter = new HomographyWriter();
	
	homographyWriter->writeToFile(homographies, idCombinations, parser->getImageLines());
	
}




CvMat* Controller::findHomography(std::deque<Features::FeaturePair> seqMatches){

	
	if (seqMatches.size() < Settings::ransacMinNumOfSupporters) {
		Output::message("Not enough pairs for finding homography (given: %d, needed: %d)", Output::IMPORTANT, seqMatches.size(), Settings::ransacMinNumOfSupporters);
		///TODO clear sequence?
		return NULL;
	}

	CvMat *homography;
	//CvMat *homographyCandidate;
	CvMat *homographyInv;
	CvMat *homographyVector;
	
	//CvMat *BMaybeInliers;
	//CvMat *AMaybeInliers;
	
	//CvMat *calculatedRightPositions;
	//CvMat *leftPositions;
	//CvRNG rngState; /* state of random number generator */
	Features::FeaturePair currentMatch;
	
	
	//CvMemStorage* storageMaybeInliers;
	//CvMemStorage* storageCurrentInliers;
	//CvMemStorage* storageMostInliers;
	CvMemStorage* storageFinalMatches;
	//CvSeq* maybeInliers;
	//CvSeq* currentInliers;
	//CvSeq* mostInliers;
	CvSeq* finalMatches;

	
	storageFinalMatches = cvCreateMemStorage(Settings::maxFeaturePoints * sizeof(Features::FeaturePair));
	finalMatches = cvCreateSeq(0, sizeof(CvSeq), sizeof(Features::FeaturePair), storageFinalMatches);

	homographyInv = cvCreateMat(3, 3, CV_32F);
	
	homography = cvCreateMat(3, 3, CV_32F);
	//homographyCandidate = cvCreateMat(3, 3, CV_32F);
	homographyVector = cvCreateMat(8, 1, CV_32F);
	//BMaybeInliers = cvCreateMat(2 * Settings::ransacNumOfMaybeInliers, 1, CV_32F);
//	AMaybeInliers = cvCreateMat(2 * Settings::ransacNumOfMaybeInliers, 8, CV_32F);
//	calculatedRightPositions = cvCreateMat(3, 1, CV_32F);
//	leftPositions = cvCreateMat(3, 1, CV_32F);
//	rngState = cvRNG(time(NULL));
//	
//	storageMaybeInliers = cvCreateMemStorage(Settings::ransacNumOfMaybeInliers   * sizeof(Features::FeaturePair));
//	storageCurrentInliers  = cvCreateMemStorage(Settings::maxFeaturePoints * sizeof(Features::FeaturePair));
//	storageMostInliers  = cvCreateMemStorage(Settings::maxFeaturePoints * sizeof(Features::FeaturePair));
//	maybeInliers = cvCreateSeq(0, sizeof(CvSeq), sizeof(Features::FeaturePair), storageMaybeInliers);
//	currentInliers  = cvCreateSeq(0, sizeof(CvSeq), sizeof(Features::FeaturePair), storageCurrentInliers);
//	mostInliers  = cvCreateSeq(0, sizeof(CvSeq), sizeof(Features::FeaturePair), storageMostInliers);
	
	

	float meanError;
	float minError;
	
	meanError = 0.0;
	minError = 32768.0;
	
//
//	// iterate the RANSAC algorithm a fixed time of iterations or stop when
//	// one Match
//	cvClearSeq(finalMatches);
//	cvClearSeq(currentInliers);
//	cvClearSeq(mostInliers);
//	
//	
//	
//	for (int i = 0; i < Settings::ransacNumOfIterations 
//			&& (mostInliers->total < seqMatches.size() * Settings::ransacStopIterationAtPercentageOfMatches); i++) {
//		// get random matches and assume them to be inliers
//		cvSetZero(AMaybeInliers);
//		cvSetZero(BMaybeInliers);
//		
//		
//		for (int j = 0; j < Settings::ransacNumOfMaybeInliers; j++) {
//			// pick a random feature match
//			//urrentMatch = (Features::FeaturePair*)cvGetSeqElem(seqMatches, cvRandInt(&rngState) % seqMatches.size());
//			currentMatch = seqMatches[cvRandInt(&rngState) % seqMatches.size()];
//			cvSeqPush(maybeInliers, &currentMatch);
//			
//			// initiate the A and B matrix for the DLT algorithm .. we set z, z' and h9 = 1
//			// A matrix first row
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2, 3) = -1 * currentMatch.left.x;
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2, 4) = -1 * currentMatch.left.y;
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2, 5) = -1;
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2, 6) = currentMatch.right.y * currentMatch.left.x;
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2, 7) = currentMatch.right.y * currentMatch.left.y;
//			// A matrix second row
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2 + 1, 0) = currentMatch.left.x;
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2 + 1, 1) = currentMatch.left.y;
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2 + 1, 2) = 1;
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2 + 1, 6) = -1 * currentMatch.right.x * currentMatch.left.x;
//			CV_MAT_ELEM(*AMaybeInliers, float, j * 2 + 1, 7) = -1 * currentMatch.right.x * currentMatch.left.y;
//			// B matrix
//			CV_MAT_ELEM(*BMaybeInliers, float, j * 2, 0) = -1 * currentMatch.right.y;
//			CV_MAT_ELEM(*BMaybeInliers, float, j * 2 + 1, 0) = currentMatch.right.x;
//		}
//		
//		// compute the homography H out from the random inliers
//		// we get an exact, but biased solution
//		cvSolve(AMaybeInliers, BMaybeInliers, homographyVector);
//		CV_MAT_ELEM(*homographyCandidate, float, 2, 2) = 1;
//		for (int j = 0; j < 8; j++) {
//			CV_MAT_ELEM(*homographyCandidate, float, j / 3, j % 3) = CV_MAT_ELEM(*homographyVector, float, j, 0);
//		}
//				
//		// try to find matches that support the model given the random inliers
//		cvClearSeq(currentInliers);
//		meanError = 0.0;
//		for (int j = 0; j < seqMatches.size(); j++) {
//			currentMatch = seqMatches[j];
//			
//			// put the values from the position in the last frame in an vector
//			CV_MAT_ELEM(*leftPositions, float, 0, 0) = currentMatch.left.x;
//			CV_MAT_ELEM(*leftPositions, float, 1, 0) = currentMatch.left.y;
//			CV_MAT_ELEM(*leftPositions, float, 2, 0) = 1;
//			
//			// compute feature position in the new frame using the homography
//			// given by the random inliers
//			cvMatMul(homographyCandidate, leftPositions, calculatedRightPositions);
//			
//			// get the error
//			//printf("Last:(%.2f, %.2f)  Now:(%.2f, %.2f)  H*Last:(%.2f, %.2f, %.2f)\n", matchPtr->left.x, matchPtr->left.y, matchPtr->right.x, matchPtr->right.y, CV_MAT_ELEM(*X, float, 0, 0), CV_MAT_ELEM(*X, float, 1, 0), CV_MAT_ELEM(*X, float, 2, 0));
//			error = sqrt(  powf(CV_MAT_ELEM(*calculatedRightPositions, float, 0, 0) / CV_MAT_ELEM(*calculatedRightPositions, float, 2, 0) - currentMatch.right.x, 2.0f)
//					     + powf(CV_MAT_ELEM(*calculatedRightPositions, float, 1, 0) / CV_MAT_ELEM(*calculatedRightPositions, float, 2, 0) - currentMatch.right.y, 2.0f));
//			
//			//meanError += error;
//			
//			// if the error is below our threshold we will accept this match
//			if (error < Settings::ransacInliersToleratedError) {
//				cvSeqPush(currentInliers, &currentMatch);
//				meanError += error;
//				//Debug::message("Accepted pair %d, error: %f", Debug::DEBUG, i, tmp);
//			} else {
//				//Debug::message("Rejecting Pair %d (%f %f) -> (%f %f), error: %f", Debug::DEBUG, i, matchPtr->left.x, matchPtr->left.y, matchPtr->right.x, matchPtr->right.y, tmp);
//			}
//			
//		}
//		
//		meanError /= currentInliers->total;
//		//Debug::message("Inliers: %d, Mean error: %f", Debug::DEBUG, currentInliers->total, meanError);
//		
//		// if we have a stronger support for this random inliers than before we take this set
//		if (currentInliers->total > mostInliers->total) {
//			// swap seqAlsoInliers with seqMostInliers
//			swapTmp        = currentInliers;
//			currentInliers = mostInliers;
//			mostInliers    = swapTmp;
//			minError 	   = meanError;
//			///TODO clear currentInliers
//		} else if(currentInliers->total == mostInliers->total && meanError < minError) {
//			swapTmp        = currentInliers;
//			currentInliers = mostInliers;
//			mostInliers    = swapTmp;
//			minError 	   = meanError;
//		}
//		//Debug::seperator();
//	}
//	
//	Output::message("Most inliers: %d, mean error: %f", Output::DEBUG, mostInliers->total, minError);
//	
//	//return homographyCandidate;
//	
//	
//	
//	
//	
//	cvReleaseMat(&AMaybeInliers);
//	cvReleaseMat(&BMaybeInliers);
//	cvReleaseMat(&homographyCandidate);
//	
//	// we need at least a set of 4 inliers
//	if (mostInliers->total < Settings::ransacMinNumOfSupporters) {
//		Output::message("RANSAC failed! Couldn't find enough supporting points for a homography (minimum:%d, points found:%d).", 
//				Output::CRITICAL, Settings::ransacMinNumOfSupporters, mostInliers->total);
//		return NULL;
//	}
//	

	
	CvMat* leftInliers;
	CvMat* rightInliers;
	
	leftInliers  = cvCreateMat(2, seqMatches.size(), CV_32F);
	rightInliers = cvCreateMat(2, seqMatches.size(), CV_32F);
	homography = cvCreateMat(3, 3, CV_32F);

	// initiate matrices A and B		
	for (unsigned int i = 0; i < seqMatches.size(); i++) {
		//currentMatch = *((Features::FeaturePair*)cvGetSeqElem(mostInliers, i));
		currentMatch = seqMatches[i];
		cvSeqPush(finalMatches, &currentMatch);
	
		
		CV_MAT_ELEM(*leftInliers, float, 0, i) = currentMatch.left.x;
		CV_MAT_ELEM(*leftInliers, float, 1, i) = currentMatch.left.y;
		
		CV_MAT_ELEM(*rightInliers, float, 0, i) = currentMatch.right.x;
		CV_MAT_ELEM(*rightInliers, float, 1, i) = currentMatch.right.y;
		
		
	}
		
	
	
	cvFindHomography(leftInliers, rightInliers, homography);
	
	cvReleaseMat(&leftInliers);
	cvReleaseMat(&rightInliers);
	
	
	Output::message("Rejected pairs: %d", Output::DEBUG, (seqMatches.size()) - (finalMatches->total));
	
	/* copy finalMatches to input sequence */
	/*cvClearSeq(seqMatches);
	for (int i = 0; i < finalMatches->total; ++i) {
		cvSeqPush(seqMatches, cvGetSeqElem(finalMatches, i));
	}*/
	
	/*seqMatches.clear();
	for (int i = 0; i < finalMatches->total; ++i) {
		//cvSeqPush(seqMatches, cvGetSeqElem(finalMatches, i));
		seqMatches.push_back(cvGetSeqElem(finalMatches, i));
	}*/

	
	
	
	
	
	
	
	
	
	cvClearSeq(finalMatches);
	//cvClearSeq(currentInliers);
	//cvClearSeq(maybeInliers);
	//cvClearSeq(mostInliers);

	cvReleaseMemStorage(&storageFinalMatches);
	//cvReleaseMemStorage(&storageCurrentInliers);
	//cvReleaseMemStorage(&storageMaybeInliers);
	//cvReleaseMemStorage(&storageMostInliers);
	
	return homography;
	
}





errorCodes::errorCode Controller::loadImage(std::string filename, IplImage* &image)
{
	Output::message("Loading image %s", Output::VERBOSE, filename.c_str());
	
	unsigned int width=0,height=0;
	
	errorCodes::errorCode returnCode;
	
	//open image with LibTiff
	TIFF* tif= TIFFOpen(filename.c_str(), "r"); 

	//IplImage *image;
	//cvReleaseImage(&image);
	size_t npixels;
	uint32 *raster;
	std::string datname;
	int datnamePos1=0, datnamePos2=0;

	//if file can be opened
	if (tif)
	{
		//get width & height
		TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);           
		TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);  

		//get image data.
		npixels=width*height;
		raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));

		//OpenCV can't handle TIFF* => copy from raster to IplImage *image
		if (raster != NULL)
		{
			if (TIFFReadRGBAImage(tif, width, height, raster, 0))
			{
				image = cvCreateImage( cvSize(width,height), IPL_DEPTH_8U, 1 );

				//give data from raster to *image step by step
				for (unsigned int i=0; i<height; i++)
				{
					for (unsigned int j=0; j<width; j++)
					{
						(((uchar*)(image->imageData + image->widthStep*((height-i-1)))))[j * image->nChannels] = (uchar)raster[i*width + j];
					}
				}

				for(int i=filename.length()-1; i>=0; i--)
				{
					if(filename[i]=='.')
						datnamePos2=i;
					else if (filename[i]=='\\')
					{
						datnamePos1=i+1;
						i=0;
					}
				}
				datname=filename.substr(datnamePos1,datnamePos2-datnamePos1);

			}
			_TIFFfree(raster);
			returnCode = errorCodes::SUCCESS;
		} else {
			
			Output::message("ERROR: Raster = NULL", Output::CRITICAL);
			
			returnCode = errorCodes::FAILURE;
		}
		
		
		TIFFClose(tif);
		
	} else {
		returnCode = errorCodes::FAILURE;
	}
	
	return returnCode;

		
}






void Controller::shutDown(){
	this->finished = true;
}

